package editorsvg.editor.herramientas;

import editorsvg.editor.comandos.Comando;
import editorsvg.editor.cursores.Cursores;
import editorsvg.editor.documento.ElementoSVG;
import editorsvg.editor.vista.EditorDocumento;
import editorsvg.utilidades.Angulo;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
 * Herramienta cuya utilidad es la selección de elementos graficos y 
 * el desplazamiento, escalado y rotación de los elementos seleccionados.
 * @author Jorge Berjano
 */
public class HerramientaSeleccion extends HerramientaBase {
    
    private EditorDocumento editor;
    private Accion accion = Accion.SELECCION;
    private Point puntoBase;
    private Point puntoActual;
    private Angulo anguloBase;
    private ElementoSVG ultimoNodoPulsado;
    private Rectangle rectanguloSeleccionElastica = new Rectangle();

    public HerramientaSeleccion(EditorDocumento editor) {
        this.editor = editor;
    }

    public Accion getAccion() {
        return accion;
    }

    public void setAccion(Accion accion) {
        this.accion = accion;
    }

    public enum Accion {

        SELECCION, SELECCION_ELASTICA, DESPLAZAMIENTO, ESCALADO, ROTACION;
    }

    @Override
    public boolean pulsado(MouseEvent e) {
        puntoActual = e.getPoint();
        puntoBase = puntoActual;

        Point2D posicion = editor.getConversor().aUnidadesReales(puntoActual);
        ultimoNodoPulsado = editor.obtenerElementoEn(posicion);

        if (editor.getSeleccion().haySeleccion()) {

            if (editor.getSeleccion().iniciarEscalado(puntoActual)) {
                setAccion(Accion.ESCALADO);
            } else if (editor.getSeleccion().iniciarRotacion(puntoActual)) {
                setAccion(Accion.ROTACION);
                anguloBase = new Angulo(editor.getSeleccion().getCentroPixels(), puntoActual);
            }
        }
        return true;
    }

    @Override
    public boolean arrastrando(MouseEvent e) {
        puntoActual = e.getPoint();
        int dx = puntoActual.x - puntoBase.x;
        int dy = puntoActual.y - puntoBase.y;

        if (getAccion() == Accion.SELECCION) {

            if (ultimoNodoPulsado != null) {
                if (!editor.getSeleccion().contiene(ultimoNodoPulsado)) {
                    editor.seleccionar(ultimoNodoPulsado, false);
                }
            }
            if (editor.getSeleccion().iniciarDesplazamiento(puntoBase)) {
                setAccion(Accion.DESPLAZAMIENTO);
            } else {
                setAccion(Accion.SELECCION_ELASTICA);
            }
        }

        if (getAccion() == Accion.DESPLAZAMIENTO) {
            if (editor.getSeleccion() != null) {
                editor.getSeleccion().actualizarDesplazamiento(dx, dy);
            }
        } else if (getAccion() == Accion.ESCALADO) {
            editor.getSeleccion().actualizarEscalado(dx, dy);
        } else if (getAccion() == Accion.ROTACION) {
            editor.getSeleccion().actualizarRotacion(obtenerAnguloRotacion(puntoActual));
        }
        return true;
    }

    private Angulo obtenerAnguloRotacion(Point posicionRaton) {
        Angulo anguloActual = new Angulo(editor.getSeleccion().getCentroPixels(), posicionRaton);
        return anguloBase.restar(anguloActual);
    }

    @Override
    public boolean liberado(MouseEvent e) {
        puntoActual = e.getPoint();
        int dx = puntoActual.x - puntoBase.x;
        int dy = puntoActual.y - puntoBase.y;
        boolean agregar = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0;
        Comando comando = null;
        switch (getAccion()) {
            case DESPLAZAMIENTO:
                comando = editor.getSeleccion().finalizarDesplazamiento(dx, dy);
                break;
            case ESCALADO:
                comando = editor.getSeleccion().finalizarEscalado(dx, dy);
                break;
            case ROTACION:
                comando = editor.getSeleccion().finalizarRotacion(obtenerAnguloRotacion(puntoActual));
                break;
            case SELECCION:
                seleccionar(puntoActual, agregar);
                break;
            case SELECCION_ELASTICA:
                rectanguloSeleccionElastica.setFrameFromDiagonal(puntoBase, puntoActual);
                seleccionar(rectanguloSeleccionElastica, agregar);
                break;
        }
        setAccion(Accion.SELECCION);
        
        if (comando != null) {            
            editor.ejecutar(comando);
        }
        return true;
    }

    private boolean seleccionar(Point point, boolean agregar) {
        Point2D puntoLogico = editor.getConversor().aUnidadesReales(point);

        return editor.seleccionar(puntoLogico, agregar);
    }

    private boolean seleccionar(Rectangle rect, boolean agregar) {
        Rectangle2D rectLogico = editor.getConversor().aUnidadesReales(rect);

        return editor.seleccionar(rectLogico, agregar);
    }

    @Override
    public Cursor getCursor() {
        return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
    }

    @Override
    public void pintarEnPixeles(Graphics2D graphics) {
        if (getAccion() == Accion.SELECCION_ELASTICA) {
            rectanguloSeleccionElastica.setFrameFromDiagonal(puntoBase, puntoActual);
            graphics.setColor(Color.BLACK);
            graphics.setStroke(new BasicStroke(1));
            graphics.setXORMode(Color.WHITE);
            graphics.draw(rectanguloSeleccionElastica);
            graphics.setPaintMode();
        }
    }
}
